package org.xx.armory.commons;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;

import static org.xx.armory.commons.Validators.rejectIfNull;

public abstract class LocalDateTimeSerializer<T extends Temporal>
        extends StdScalarSerializer<T> {
    private final DateTimeFormatter dateTimeFormatter;

    private LocalDateTimeSerializer(
            Class<T> clazz,
            DateTimeFormatter dateTimeFormatter
    ) {
        super(clazz);
        this.dateTimeFormatter = rejectIfNull(dateTimeFormatter, "dateTimeFormatter");
    }

    public static LocalDateTimeSerializer<LocalDate> dateWithFormat(
            DateTimeFormatter dateTimeFormatter
    ) {
        return new LocalDateSerializer(dateTimeFormatter);
    }

    public static LocalDateTimeSerializer<LocalTime> timeWithFormat(
            DateTimeFormatter dateTimeFormatter
    ) {
        return new LocalTimeSerializer(dateTimeFormatter);
    }

    public static LocalDateTimeSerializer<LocalDateTime> dateTimeWithFormat(
            DateTimeFormatter dateTimeFormatter
    ) {
        return new LocalDateTimeSerializer_(dateTimeFormatter);
    }

    protected final DateTimeFormatter getDateTimeFormatter() {
        return this.dateTimeFormatter;
    }

    private static class LocalDateSerializer extends LocalDateTimeSerializer<LocalDate> {
        LocalDateSerializer(
                DateTimeFormatter dateTimeFormatter
        ) {
            super(LocalDate.class, dateTimeFormatter);
        }

        @Override
        public void serialize(
                LocalDate value,
                JsonGenerator jsonGenerator,
                SerializerProvider serializerProvider
        )
                throws IOException {
            assert value != null;

            jsonGenerator.writeString(value.format(this.getDateTimeFormatter()));
        }
    }

    private static class LocalTimeSerializer extends LocalDateTimeSerializer<LocalTime> {
        LocalTimeSerializer(
                DateTimeFormatter dateTimeFormatter
        ) {
            super(LocalTime.class, dateTimeFormatter);
        }

        @Override
        public void serialize(
                LocalTime value,
                JsonGenerator jsonGenerator,
                SerializerProvider serializerProvider
        )
                throws IOException {
            assert value != null;

            jsonGenerator.writeString(value.format(this.getDateTimeFormatter()));
        }
    }

    private static class LocalDateTimeSerializer_ extends LocalDateTimeSerializer<LocalDateTime> {
        LocalDateTimeSerializer_(
                DateTimeFormatter dateTimeFormatter
        ) {
            super(LocalDateTime.class, dateTimeFormatter);
        }

        @Override
        public void serialize(
                LocalDateTime value,
                JsonGenerator jsonGenerator,
                SerializerProvider serializerProvider
        )
                throws IOException {
            assert value != null;

            jsonGenerator.writeString(value.format(this.getDateTimeFormatter()));
        }
    }
}
